vlwkaos' digital garden

JavaScript - Closure

Closure란 무엇인가?

A closure is a function plus a connection to the variables that exist at its “birth place”.

  1. 함수
  2. 함수가 만들어진 곳에 있던 변수를 계속 사용 가능하다.

2가 왜 필요할까? 👀 [[JavaScript - Call, Apply, Bind]]

function hello(parentVar) {
  return function (childVar) {
    console.log(`${parentVar} -> ${childVar}`)
  }
}

const world = hello("1") // 내부 함수 생성시에 전달된 변수를 계속 알고있다.
world("2") // `1 -> 2`

hello 함수는 world에 closure를 반환한다.

world는 할당될 때 알고있던 값을 계속 알 수 있기 때문에 함수 호출이 끝나서 함수의 Scope를 벗어났음에도 인자 parentVar에 전달한 값 "1"에 대해 알고 있다.

언제 사용할까?

  • 그냥 static scope를 제공할 때.
  • 특정값이 고정된 상태의 함수를 만들고 싶을때.
  • 실질적으로 접근 불가능한 private data를 만들고 싶을때.

예제를 보자.

class Countdown {
  constructor(counter, action) {
    Object.assign(this, {
      dec() {
        if (counter < 1) return
        counter--
        if (counter === 0) {
          action()
        }
      },
    })
  }
}
  • counter는 생성자를 통해 넘겨지지만 접근할 수 없다.

clousure의 장점 - 일단 이런식으로 은닉화 하는 것은 코드를 이해하기 힘듦. 그래서 #private같은 syntactic sugar가 추가되는 것 같다. - 부분 적용 함수를 만들 수 있다. 함수의 확장에 용이함.

단점 - 루프 안에서 외부 scope의 변수/함수를 참조하는 경우, clousre때문에 같은 [[JavaScript - lexical environment]]를 공유하기 때문에 실수할 수 있다.

function showHelp(help) {
  document.getElementById("help").textContent = help
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your e-mail address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ]

  for (var i = 0; i < helpText.length; i++) {
    // var는 함수 내에서 hoisting되어,
    // 다음 함수에서 하나의 lexical environment로 참조되고
    // 결국 함수 호출 시점에는, 마지막 값인 'age'만 사용됨.
    var item = helpText[i]
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help)
    }
  }
}

setupHelp()
  • 메모리와 성능에 타격을 줄 수 있음. Scope 생성에 따른 성능상 손해가 있어서 closure를 사용하지 않아도 되는 경우에는 쓰지 않는 것이 좋음.
function MyObject(name, message) {
  this.name = name.toString()
  this.message = message.toString()
  this.getName = function () {
    return this.name
  }

  this.getMessage = function () {
    //  안에서 이렇게 하기보다 prototype에..
    return this.message
  }
}
JavaScript - Closure